﻿/**********************************************************/
/*                Code by: Sam Wronski                    */
/*             email: runewake2@gmail.com                 */
/* Distributed under the Microsoft Public License (Ms-PL) */
/**********************************************************/
/*     https://gamejolttrophies.codeplex.com/license      */
/**********************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Net;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using System.ComponentModel;

namespace GameJoltAPI
{
    public class UserEventArgs : EventArgs
    {
        private List<User> users;

        public List<User> Users { get { return users; } set { users = value; } }

        public UserEventArgs(List<User> u) { this.users = u; }
    }

    public delegate void UserEventHandler(object sender, UserEventArgs e);

    /// <summary>
    /// Handles Fetching and Authenticating Users
    /// </summary>
    public class UserClient
    {
        private static readonly string _url_fetch = @"http://gamejolt.com/api/game/v1/users/?format=json";
        private static readonly string _url_auth = @"http://gamejolt.com/api/game/v1/users/auth/?format=dump";

        /// <summary>
        /// <para>This event occurs when a list of users is fetched</para>
        /// </summary>
        public event UserEventHandler OnFetchComplete;

        /// <summary>
        /// <para>This event occurs whenever an error occurs when fetching a User</para>
        /// </summary>
        public event EventHandler OnFetchError;

        /// <summary>
        /// <para>This event is called when a User is successfully authenticated</para>
        /// </summary>
        public event EventHandler OnAuthenticationComplete;

        /// <summary>
        /// <para>This event is called whenever an error occurs when attempting to authenticate a user or the user could not be authenticated</para>
        /// <para>This events sender will be a string containing the error message.</para>
        /// <see cref="http://gamejolt.com/api/doc/game/formats/dump/"/>
        /// </summary>
        public event EventHandler OnAuthenticationError;

        /// <summary>
        /// Attempts to get information about the current user
        /// </summary>
        public void FetchUser()
        {
            string url = _url_fetch;
            url += "&game_id=" + Settings.GameID;
            url += "&username=" + Settings.Username;
            BeginFetchResult(url);
        }

        /// <summary>
        /// Attemps to get information about a user with the specified Username
        /// </summary>
        /// <param name="un">the Username to attempt to fetch</param>
        public void FetchUser(string un = null)
        {
            string url = _url_fetch;

            url += "&game_id=" + Settings.GameID;
            if (un != null)
                un += "&username=" + un;
            else
                un += "&username=" + Settings.Username;

            BeginFetchResult(url);
        }

        /// <summary>
        /// Attempts to retrieve information about a list of users by ID
        /// </summary>
        /// <param name="ids">The list of user id's</param>
        public void FetchUsersByID(List<string> ids)
        {
            string request = "";

            foreach (string s in ids)
                request += s + ",";

            request = request.Substring(0, request.Count() - 1);
            FetchUserByID(request);
        }

        /// <summary>
        /// Attempts to get information about a user based on the given ID
        /// </summary>
        /// <param name="id">The ID to get information for</param>
        public void FetchUserByID(string id = null)
        {
            string url = _url_fetch;

            url += "&game_id=" + Settings.GameID;
            if (id != null)
                id += "&user_id=" + id;

            BeginFetchResult(url);
        }

        /// <summary>
        /// Attempts to Authenticate the current user
        /// </summary>
        public void AuthenticateUser()
        {
            string url = _url_auth;
            url += "&game_id=" + Settings.GameID;
            url += "&username=" + Settings.Username;
            url += "&user_token=" + Settings.Token;

            BeginAuthResult(url);
        }

        /// <summary>
        /// Attempts to authenticate a user with given Username and Token
        /// </summary>
        /// <param name="un">The username to authenticate</param>
        /// <param name="token">The token to authenticate</param>
        public void AuthenticateUser(string un, string token)
        {
            string url = _url_auth;
            url += "&game_id=" + Settings.GameID;
            url += "&username=" + un;
            url += "&user_token=" + token;

            BeginAuthResult(url);
        }

        //This gets the result
        private void BeginFetchResult(string url)
        {
            string rs = url + "&signature=" + Settings.Signiture(url); //Append signiture

            NetworkAccess.StartConnection();

            //TODO: Web API Call
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(rs);
            request.Method = "GET";
            request.BeginGetResponse(EndFetchResult, request);
        }

        //This fetches the result data
        private void EndFetchResult(IAsyncResult result)
        {
            NetworkAccess.FinishConnection();

            if (result.IsCompleted)
            {
                HttpWebRequest request = (HttpWebRequest)result.AsyncState;

                //Deserialize the data and call the event
                string data = new StreamReader(request.EndGetResponse(result).GetResponseStream()).ReadToEnd();

                UserResult res = JsonConvert.DeserializeObject<UserResult>(data);
                CallFetchCompleted(res);
            }
        }

        //This interprets the result data
        private void CallFetchCompleted(UserResult result)
        {
            if (result != null && result.Response.Success.ToLower()[0] == 't')
            {
                if (OnFetchComplete != null)
                    OnFetchComplete(result, new UserEventArgs(result.Response.Users));
            }
            else
            {
                if (OnFetchError != null)
                    OnFetchError(result, new EventArgs());
            }
        }

        //This gets the result
        private void BeginAuthResult(string url)
        {
            string rs = url + "&signature=" + Settings.Signiture(url); //Append signiture

            NetworkAccess.StartConnection();

            //TODO: Web API Call
            HttpWebRequest request = (HttpWebRequest)WebRequest.Create(rs);
            request.Method = "GET";
            request.BeginGetResponse(EndAuthResult, request);
        }

        //This fetches the result data
        private void EndAuthResult(IAsyncResult result)
        {
            NetworkAccess.FinishConnection();

            if (result.IsCompleted)
            {
                HttpWebRequest request = (HttpWebRequest)result.AsyncState;

                //Deserialize the data and call the event
                string data = new StreamReader(request.EndGetResponse(result).GetResponseStream()).ReadToEnd();

                if (data.ToLower()[0] == 's')
                {
                    if (OnAuthenticationComplete != null)
                        OnAuthenticationComplete(data, new EventArgs());
                }
                else
                {
                    if (OnAuthenticationError != null)
                        OnAuthenticationError(data, new EventArgs());
                }
            }
        }
    }

    [JsonObject()]
    public class UserResult
    {
        [JsonProperty("response")]
        public UserResponse Response { get; set; }
    }

    [JsonObject()]
    public class UserResponse
    {
        [JsonProperty("success")]
        public string Success { get; set; }

        [JsonProperty("users")]
        public List<User> Users { get; set; }
    }

    /// <summary>
    /// This class represents a User from Game Jolt
    /// <see cref="http://gamejolt.com/api/doc/game/users/"/>
    /// </summary>
    [JsonObject()]
    public class User
    {
        /// <summary>
        /// The Achievements ID
        /// </summary>
        [JsonProperty("id")]
        public string ID { get; set; }

        /// <summary>
        /// The Title of the Achievement, its name
        /// </summary>
        [JsonProperty("type")]
        public string Type { get; set; }

        /// <summary>
        /// A desctiption of the Achievement
        /// </summary>
        [JsonProperty("username")]
        public string Name { get; set; }

        /// <summary>
        /// The Difficulty of the Achievement, as a String
        /// </summary>
        [JsonProperty("avatar_url")]
        public string AvatarURL { get; set; }

        /// <summary>
        /// The URL of the Achievements Image
        /// </summary>
        [JsonProperty("last_logged_in")]
        public string LastLogin { get; set; }

        /// <summary>
        /// The Achievement Status of the achievement as a string. That is, "true" or "false"
        /// </summary>
        [JsonProperty("status")]
        public string Status { get; set; }

        //The properties below will ONLY exist if the user is a developer

        /// <summary>
        /// This holds a value that is true if the user has values for the developer properties and is false otherwise.
        /// </summary>
        public bool IsDeveloper { get { return DeveloperDescription != null && DeveloperWebsite != null && DeveloperName != null; } }

        /// <summary>
        /// The Developers name
        /// </summary>
        [JsonProperty("developer_name")]
        public string DeveloperName { get; set; }

        /// <summary>
        /// The developers website, if they have one
        /// </summary>
        [JsonProperty("developer_website")]
        public string DeveloperWebsite { get; set; }

        /// <summary>
        /// The developers description of themselves. Formatting is removed from this result.
        /// </summary>
        [JsonProperty("deverloper_description")]
        public string DeveloperDescription { get; set; }
    }
}
